<?php
/**
 * Game_Unit
 *
 * Класс Game_Unit отвечает за обработку юнитов
 *
 * @author Дмитрий Кузьма <kouzma.d@gmail.com>
 * @version 1.0
 */
class Game_Unit extends ActiveRecord {
	
	protected static $_tableName = 'unit';
	protected static $_pkName = 'ID_unit';
	protected static $_arrAllUnitsInfo;
	public $objResources;// Game_Resource;
	public $objUnitResearch; //Game_UnitResearch
  
		/**
	 * Конструктор объекта (создание, инициализация, загрузка данных) 
	 * 
	 * @param mixed $arrInitialFields - ID объекта, либо массив с данными, либо строка что ищем
	 * @param array $arrSettings - Массив с настройками
	 */
	
	public function __construct($arrInitialFields = null, array $arrSettings = array()) {
		// Особое создание по ID юнита
		if (is_numeric($arrInitialFields)) {
			$unitId = $arrInitialFields;
			$this->_fillAllUnitsInfo();
			parent::__construct(self::$_arrAllUnitsInfo[$unitId], $arrSettings);
		}
		else {
			parent::__construct($arrInitialFields, $arrSettings);
		}
		
		if (is_numeric($arrInitialFields)) {
			$this->objResources = new Game_Resource($this->tableFields);
		}
	}
	
	
	/**
	 * Возвращает название юнита
	 * 
	 */
	public function getName() {
		if (!$this->id) return false;
		self::_fillAllUnitsInfo();
		return self::$_arrAllUnitsInfo[$this->id]['unit_name_' . Service_Translate::getLang()];
	}
	
	/**
	 * Получает название юнита по его идентификатору
	 * 
	 * @param int $resourceId
	 */
	public static function getUnitName($unitId) {
		self::_fillAllUnitsInfo();
		return self::$_arrAllUnitsInfo[$unitId]['unit_name_' . Service_Translate::getLang()];
	}
	
	/**
	 * Получает список юнитов по принципу синглтона
	 * 
	 * @return array
	 */
	public static function getAllUnitsInfo() {
		self::_fillAllUnitsInfo();
		return self::$_arrAllUnitsInfo;
	}
	
	/**
	 * Заполняет данные о юнитах
	 */
	private static function _fillAllUnitsInfo() {
		if (empty(self::$_arrAllUnitsInfo)) {
			$objDB = Service_Database::instance();
			$stmt = "SELECT *	FROM `unit`";
			self::$_arrAllUnitsInfo = Service_ArrayConverter::makeKeyArrayFromMultyArray($objDB->select($stmt), 'ID_unit');
		}		
	}
	

	
	/**
	 * Получает список исследованных юнитов
	 * 
	 * @return array
	 */
	public static function getUnitsResearchedIds() {
		$objGeneral = Game_General::instance();
		$placeId = $objGeneral->objPlace->id;
		$objDb = Service_Database::instance();
		$stmt = "
			SELECT `ID_unit`
			FROM `place_research`
			WHERE `ID_place` = '" . $placeId . "' 
		";
		return array_merge(array(1), $objDb->selectSimpleArray($stmt));
	}
	

	

	
	/**
	 * Создает событие "Исследование юнита"
	 *
	 * @param int $unitIduild
	 */
	private function _researchUnitEvent($unitId) {
		$objGeneral = Game_General::instance();
		$objGameEvent = new Game_Event(array(
				'ID_place' => $objGeneral->objPlace->placeId,
				'ID_user' => $objGeneral->objUser->id,
				'event_type' => 'research',
				'ID_essence' => $unitId,
				'amount' => 1,
				'event_time' => Service_Date::getTimestampToDBSaveWithSecondsMove($this->getResearchTime())
		));
		$objGameEvent->save();
	}
	
	/**
	 * Исследует новый юнит
	 */
	public function researchUnit() {
		if (!$this->isAvailableToResearch()) return false;
		return $this->_makeResearch();
	}
	
	/**
	 * Метод проверяет, возможно ли построить здание прямо сейчас
	 *
	 */
	public function isAvailableToResearch() {
		// Проверяем, выполнены ли зависимости
		$arrUnitsInfo = self::getResearchStatuses();
		if (!$this->_isAvailableToResearchUnit($arrUnitsInfo[$this->id])) return false;
		
		// Проверяем, достаточно ли ресурсов для строительства
		if (!$this->_isEnoughResourcesToResearch($arrUnitsInfo[$this->id]['research_info'])) return false;
		
		return true;
	}
	
	/**
	 * Проверяет, достаточное ли ресурсов для строительства
	 *
	 * @param Game_Resource $objResourceToPay
	 */
	protected function _isEnoughResourcesToResearch(Game_Resource $objResourceToPay) {
		$objGeneral = Game_General::instance();
		return $objGeneral->objPlace->objResourcesWithProduction->isEnoughResources($objResourceToPay);
	}
	
	/**
	 * Проверяет, есть ли возможность исследовать юнит
	 * 
	 * @param array $arrUnitInfo
	 */
	private function _isAvailableToResearchUnit(array $arrUnitInfo) {
		
		return (!$arrUnitInfo['is_researched'] && empty($arrUnitInfo['unperformed_dependences']));
	}
	
	/**
	 * Возвращает базовое время строительства юнита
	 */
	public function getBaseBuildingTime() {
		return $this->getField('time_base');
	}
	
	/**
	 * Возвращает время строительства юнита в конкретном месте
	 */
	public function getPlaceBuildingTime() {
		$baseTime = $this->getBaseBuildingTime();
		$speed = Game_Building_Military::getMilitarySpeedByType($this->getField('unit_type'));
		return $baseTime / $speed;
	}
	
	/**
	 * Создает юнитов
	 * 
	 * @param int $unitsAmount
	 * 
	 * @return boolean
	 */
	public function createUnits($unitsAmount) {
		$objGeneral = Game_General::instance();
		$objCurUnitStatus = Game_Unit_Status::getPlaceUnitStatus($this->id);
		if (!$objCurUnitStatus->isResearched()) throw new Exception_Game_Research();
			
		$resourcesEnoughForTimes = $objGeneral->objPlace->objResourcesWithProduction->howManyTimesEnoughResources($this->objResources);
		if ($resourcesEnoughForTimes < $unitsAmount) throw new Exception_Game_Resources();
		
		return $this->_makeCreating($unitsAmount);
	}
	
	/**
	 * Метод оплачивает исследование юнита и создает соответствующее игровое событие
	 *
	 * @param int $unitsAmount
	 *
	 * @return boolean
	 */
	private function _makeCreating($unitsAmount) {
		// TODO по хорошему можно SQL транзакцию вставить, но не обязательно
		$objGeneral = Game_General::instance();
		$objGeneral->pay($this->objResources->getMultipliedResources($unitsAmount));
		$this->_createUnitsCreateEvent($unitsAmount);
		return true;
	}
	
	/**
	 * Создает событие "Создание юнита"
	 *
	 * @param int $unitsAmount
	 */
	private function _createUnitsCreateEvent($unitsAmount) {
		$objGeneral = Game_General::instance();
		$buildingUnitTime = $this->getPlaceBuildingTime();
		$lastUnitCreationTime = Game_Event::getPlaceLastEventTime();
		if (empty($lastUnitCreationTime)) $lastUnitCreationTime = Service_Date::time2BD();
		
		$objGameEvent = new Game_Event(array(
			'ID_place' => $objGeneral->objPlace->id,
			'ID_user' => $objGeneral->objUser->id,
			'event_type' => 'unit',
			'ID_essence' => $this->id,
			'amount' => $unitsAmount,
			'event_time' => Service_Date::timeWithDelta($lastUnitCreationTime, $buildingUnitTime),
			'event_time_additional_end' => Service_Date::timeWithDelta($lastUnitCreationTime, $buildingUnitTime * $unitsAmount)
		));
		$objGameEvent->save();
	}
	
	/**
	 * Получает скорость группы
	 * 
	 * @param array $arrUnitsIds
	 */
	public static function getSpeedOfUnitsGroup(array $arrUnitsIds) {
		if (!$arrUnitsIds) return false;
		$arrUnits = self::getAllUnitsInfo();
		$speed = 999;
		foreach ($arrUnitsIds as $unitId) {
			$speed = min($speed, $arrUnits[$unitId]['speed']);
		}
		return $speed;
	}
	
	/**
	 * Вычисляет очки атаки группы
	 * 
	 * @param Game_Unit_UnitsGroup $objUnitsGroup
	 */
	public static function calculateUnitsGroupAttack(Game_Unit_UnitsGroup $objUnitsGroup) {
		self::_fillAllUnitsInfo();
		$arrAttackerUnits = $objUnitsGroup->getUnitsInGroup();
		$arrAttackPoints = array(1 => 0, 2 => 0);
		
		foreach ($arrAttackerUnits as $arrAttackersUnitData) {
			$unitId = $arrAttackersUnitData['ID_unit'];
			$attackType = self::getAttackTypeByUnitType(self::$_arrAllUnitsInfo[$unitId]['unit_type']);
			$baseAttack = self::$_arrAllUnitsInfo[$unitId]['attack_1'];
			$bonusAttack = self::getUnitUpgradeBonus(
				$baseAttack, 
				self::$_arrAllUnitsInfo[$unitId]['consumption_1'],
				$arrAttackersUnitData['unit_level']
			);
			$arrAttackPoints[$attackType] += ($baseAttack + $bonusAttack) * $arrAttackersUnitData['amount'];
		}
		return $arrAttackPoints;
	}
	
	/**
	 * Получает информацию о защите, которую дают юниты
	 */
	public static function calculateUnitsDefense(array $arrUnitsData) {
		$arrDefensePoints = array(1 => 0, 2 => 0);
		
		foreach ($arrUnitsData as $arrUnitData) {
			$unitId = $arrUnitData['ID_unit'];
			for ($i = 1; $i <= 2; $i++) {
				$baseDefense = self::$_arrAllUnitsInfo[$unitId]['defense_' . $i];
				$bonusDefense = self::getUnitUpgradeBonus(
						$baseDefense,
						self::$_arrAllUnitsInfo[$unitId]['consumption_1'],
						$arrUnitData['unit_level']
				);
				$arrDefensePoints[$i] += ($baseDefense + $bonusDefense) * $arrUnitData['amount'];
			}
			
		}
		return $arrDefensePoints;
	}
	
	
	/**
	 * Получает тип атаки по типу юнита
	 * 
	 * @param int $unitType
	 */
	public static function getAttackTypeByUnitType($unitType) {
		if ($unitType == 2) return 2;
		else return 1;
	}
	
	/**
	 * Получает бонус апгрейда юнита
	 * 
	 * @param int $basePoints - базовые очки сражений
	 * @param int $consumption - потребление ресурсов
	 * @param int $upgradeLevel - уровень улучшений юнита
	 * 
	 * @return number
	 */
	public static function getUnitUpgradeBonus($basePoints, $consumption, $upgradeLevel = 0) {
		return ($basePoints + 300 * $consumption / 7) * (pow(1.007, $upgradeLevel) - 1);
	} 
	
}
?>